在先前的章節中,我們透過不同的工具及概念來讓大家理解 FP 在做些什麼事,舉例來說,在 Immutable 的章節我們大量使用 map
與 filter
的方法來處理陣列的資料:
const newList = list.map((item) => ({...item, grade: item.grade+1}));
在上方的範例中,會發現我們在 map 函式中,實際是傳入了一個 callback 函式,來決定我們要怎麼處理這些陣列。
而先前所提到的科里化函式實則回傳了函式,讓我們可以在固定參數的狀況下,增加函式的復用性:
const curry = a => b => a + b;
不覺得我們目前所學習到的概念與工具,確實與 FP 這門設計模式的主要概念息息相關嗎?實際上,map
、filter
及柯里化等函式,在 FP 中有個更具象化的概念可以用來解釋他們,那就是:「高階函式」。
高階函式是一種以函式作為參數,或是以函式做為回傳值的函式,舉例來說:JavaScript 中的 map
、filter
、reduce
陣列方法,及科里化函式都屬於高階函式。
與純函式相比的話,高階函式更像是一種可以重複、以兩段式、多段式被使用的公式,當然如果大家還記得 FP 的演進史的話,會發現其實 FP 中的工具的概念實質上就是數學的概念,所以把先前所提到的工具當作一種可以自定義的公式也不為過。
那什麼是公式呢?我自己對於公式的想法是:你不需要知道公式背後是怎麼被設計出來的,但如果你想要處理同類型的問題,那麼套公式就對了!
就好比如果我們想要篩選陣列中的特定資料,我們可以無腦的使用 JavaScript 的 filter
陣列方法,我們完全不用考量這個 filter
是怎麼被實作出來的,且能非常確定這個函式一定會依照我們所帶入的條件產出可預期且資料結構相符的陣列。
只不過同樣都是基於「單輸入單輸出」理念,純函式不一定可以被二次利用,大部分只是用來作為處理較為簡單的任務,例如:判斷某個型別是否為布林,或是是否有值存在。
但高階函式就不一樣了,以柯里化的函式來說,會基於在這個函式中帶入了幾個參數,而決定我們想要固定幾個參數,進而拿到回傳的局部性應用函式(Partial application,詳見註一)進行更進階的應用,而不是單單拿到單一的「值」。
而固定參數,就可以讓我們針對特定資料進行特殊化的處理,讓我們可以針對特定的情境讓指定資料重複使用。
至於要怎麼進行更進階的應用,這就要說到 FP 中另外一個更加進階的概念「複合函式」了。
複合函式指的是:透過結合多個函式而產出新的函式,或是進行運算。
這個定義是不是聽起來有點熟悉?完全是將高階函式存在的意義給點局部性應用複組合既有的函式」來達到同要的效果。
光是這樣聽起來,就知道如果我們好好利用 JavaScript 的特性,並且透過複合函式進行良好的程式碼管理,我們可以減少大量冗贅的程式碼。
還記得先前我們所提到為什麼要使用 FP 設計模式進行開發嗎?因為這可以讓我們的程式碼更簡潔、可讀性更高,一旦程式碼的量減少,蟲蟲就越少能藏匿的空間。
相信上述關於 FP 主要概念的敘述,其實沒有大家想像中的那麼難,對嗎?
在下一個章節中,我們要透過將 map 陣列方法進行科理化,讓大家能透過程式碼,更深刻的理解高階函式與複合函式究竟能為我們解決什麼問題!
那我們就下一個章節見吧!
const curry = a => b => a + b;
const curried = curry(1);
此時 curried
函式就是一個局部性應用函式,因為我們預先帶入了一個參數 1
,局部性應用可以說是抽象化概念「特殊化」的實作應用。